package com.qf.controller;

import com.qf.captcha.CaptchaCodeManager;
import com.qf.pojo.DtsAdmin;
import com.qf.pojo.DtsPermission;
import com.qf.pojo.DtsRole;
import com.qf.service.DtsPermissionService;
import com.qf.service.DtsRoleService;
import com.qf.util.*;
import com.qf.util.Base64;
import com.qf.util.UUID;
import com.qf.util.bcrypt.BCryptPasswordEncoder;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.authz.annotation.RequiresUser;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 后台管理系统 -- 认证 管理
 * <p>
 * 上行数据 ：      本地上传文件到服务器上    上：服务器
 * <p>
 * 下行数据 ：    下：本地客户端
 * <p>
 * 1、验证码  http://localhost:8080/admin/auth/captchaImage   get
 *
 * @author lixu
 */
@RestController
@RequestMapping("/admin")
@CrossOrigin
public class AdminAuthController {//我正在写服务器端的代码


    @Autowired
    private DtsRoleService dtsRoleService;
    @Autowired
    private DtsPermissionService dtsPermissionService;
    @Autowired
    private ApplicationContext applicationContext;


    /**
     * 验证码
     * - 生成验证码请求地址 : http://localhost:8083/admin/auth/captchaImage
     * - GET请求
     * - 下行数据 :
     * - uuid : 图片id(服务器端将图片保存在shiro的会话中的唯一id),  img : 图片地址
     * <p>
     * {
     * "errno":0, // Integer
     * "data":	{ // Map
     * "img":"/9j/4AAQSkZJRlZ/2Q==",  验证码
     * "uuid":"b5ca22d8b9e14a88b2851da906d19e4f"
     * },
     * "errmsg":"成功"  String
     * }
     */
    @GetMapping("/auth/captchaImage")
    public Object captchaImage() throws Exception {//String name : 上行数据 ：  就是形参  入参
        Map<String, Object> result = new HashMap<>();

        //验证码的生成的图片Base64加密
        //1:获取了验证码
        String code = VerifyCodeUtils.generateVerifyCode(4);
        //2:将code保存在缓存中Map中
        String uuid = UUID.randomUUID(true).toString();
        boolean flag = CaptchaCodeManager.addToCache(uuid, code, null);
        if (flag) {
            //将上面的code验证码数字 转成图片
            int w = 111;
            int h = 36;
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            VerifyCodeUtils.outputImage(w, h, os, code);
            result.put("img", Base64.encode(os.toByteArray()));
            result.put("uuid", uuid);
        }
        return ResponseUtil.ok(result);//下行数据就是返回值
    }

    /**
     * - 登录请求地址 : http://localhost:8083/admin/auth/login
     * - POST请求
     * {
     * "code": "66e8",
     * "password": "123456",
     * "username": "qianfeng",
     * "uuid": "cab317c6-1bf1-436d-96ac-fda9f8d37dde"
     * }
     * <p>
     * {
     * "errno":0,
     * "data":"568f6dd8-97c7-450d-a00b-0a01c3b930c7", //是当前用户Session的ID
     * "errmsg":"成功"
     * }
     * <p>
     * Vo: View Object  视图对象： 页面传递过来的数据 接收
     */
    @PostMapping("/auth/login")
    public Object login(@RequestBody String json, HttpServletRequest request) {
        //用户名
        String username = JacksonUtil.parseString(json, "username");
        String password = JacksonUtil.parseString(json, "password");
        String code = JacksonUtil.parseString(json, "code");
        String uuid = JacksonUtil.parseString(json, "uuid");

        //1:判断 code 验证码是否正确
/*        String c = CaptchaCodeManager.getCachedCaptcha(uuid);
        if (StringUtils.isEmpty(c)) {
            //验证码已经过期  硬编码问题
            return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_EXPIRED);
        }
        if (!c.equalsIgnoreCase(code)) {
            //验证码已经错误
            return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_ERROR);
        }*/
        //2:Shiro
        Subject subject = SecurityUtils.getSubject();
        //用户名密码
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
                username,
                password
        );
        try {
            //3:判断用户名或密码是否正确
            subject.login(usernamePasswordToken);
        } catch (LockedAccountException e) {
            //用户锁定不能用
            return AdminResponseUtil.fail(AdminResponseCode.ADMIN_LOCK_ACCOUNT);
        } catch (AuthenticationException e) {
            //用户名密码不正确
            return AdminResponseUtil.fail(AdminResponseCode.ADMIN_INVALID_ACCOUNT_OR_PASSWORD);
        }
        //ResponseUtil 统一返回值对象
        return ResponseUtil.ok(subject.getSession().getId());//下行数据就是返回值
    }


    /**
     * - 登录后获取用户名, 角色, 权限详细信息地址 : http://localhost:8083/admin/auth/info
     * - GET请求
     * {
     * "errno":0,
     * "data":{
     * "roles":["超级管理员"],  Set<String></String>
     * "name":"qianfeng",
     * "perms":["*"],
     * "avatar":"https://dss1.bdstatic.com/6OF1,499686101&fm=85&app=79&f=JPEG?w=121&h=75&s=81B24C32D40"
     * },
     * "errmsg":"成功"
     * }
     */
    @GetMapping("/auth/info")
    public Object info() {
        Map<String, Object> result = new HashMap<>();

        Subject subject = SecurityUtils.getSubject();
        DtsAdmin dtsAdmin = (DtsAdmin) subject.getPrincipal();//Session 对象   Vue传递过来的UUID 获取对象的Session
        //1:查询角色集合 根据用户的ID
        Set<String> roles = dtsRoleService.findRolesByIds(dtsAdmin.getRoleIds());

        result.put("roles", roles);
        result.put("name", dtsAdmin.getUsername());// 小张
        //2:查询权限集合    所有权限   "[*]":字符串
        //result.put("perms",Arrays.asList("*"));//             "[*]"
        //result.put("perms",Arrays.asList("GET /admin/admin/list","POST /admin/admin/create"));//  ["*"]
        Set<String> perms = dtsPermissionService.findPermissionListByRoleIds(dtsAdmin.getRoleIds());
        result.put("perms", toApi(perms));//  ["admin:user:list","admin:user:create"..]
        //perms: *
        // ['GET /admin/admin/list',
        // 'POST /admin/admin/create',
        // 'POST /admin/admin/update', 'POST /admin/admin/delete']
        //result.put("perms", Arrays.asList("POST /admin/goods/create","GET /admin/goods/list"));
        result.put("avatar", dtsAdmin.getAvatar());//头像
        return ResponseUtil.ok(result);
    }

    /**
     * 权限转换
     * 将后端使用的 admin:user:list 权限 转换成 前端Vue能够解析的权限  GET /admin/user/list
     *
     * @param perms
     * @return Set<String> perms： 方法形参 是当前用户拥有权限  小
     */
    private Collection toApi(Set<String> perms) {
        String basicPackage = "com.qf";
        //1:整合项目 所有权限  大
        List<Permission> permissionList = PermissionUtil.listPermission(applicationContext, basicPackage);

        //2:普通 写法
/*        List<String> ps = new ArrayList<>();
        for (Permission permission : permissionList) {
            if(perms.contains(permission.getRequiresPermissions().value()[0])){
                // 有此权限
                ps.add(permission.getApi());
            }
        }
        return ps;*/
        Set<String> permSet = permissionList.stream().filter(permission -> {
            if (perms.contains(permission.getRequiresPermissions().value()[0])) {
                return true;
            } else {
                return false;
            }
        }).map(Permission::getApi).collect(Collectors.toSet());
        return permSet;

    }


    /**
     * 当请求用户没有权限的时候 转发到403
     * <p>
     * {
     * errorno:506,
     * errormsg:无权限操作
     * }
     */
    @RequiresAuthentication
    @GetMapping("/auth/403")
    public Object auth403() {
        return ResponseUtil.unauthz();
    }


    /**
     * 退出
     */
    @RequiresAuthentication
    @PostMapping("/auth/logout")
    public Object logout() {
        System.out.println("========退出====");
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return ResponseUtil.ok();
    }


    public static void main(String[] args) {

        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();


        String encode = bCryptPasswordEncoder.encode("123456");
        System.out.println(encode);

        //$2a$10$NBpG0X/Fq8JXactUqaamr.bJRmNuv119xIllaka8zLw6ZOo.x.Yim

        //$2a$10$TcakDpTDWmvU3Ft67th.Yetr3VeE4y1xqU1ZdBVo62ESsxslZUSpW

        //$2a$10$qDsZOzLvw0Hh8mQa4nh3weBNDslYGs3hfC8FgmNg3/BPcC1O9k7oO

        boolean matches = bCryptPasswordEncoder.matches("123456", encode);
        System.out.println("matches:" + matches);
        //
        //$2a$10$XBh8sOGwISgFKkQO9Av4PurbFxrrMS03Pagtt4n0AUn.UZZnSMUL2
        //matches:true
    }
}
